/**
 * Copyright (c) 2017 Contributors to the Eclipse Foundation
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a
 * copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations
 * under the License.
 */

package org.eclipse.microprofile.openapi.apps.airlines.resources.bookings;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.callbacks.Callback;
import org.eclipse.microprofile.openapi.annotations.callbacks.CallbackOperation;
import org.eclipse.microprofile.openapi.annotations.enums.ParameterIn;
import org.eclipse.microprofile.openapi.annotations.enums.ParameterStyle;
import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeType;
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.ExampleObject;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameters;
import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement;
import org.eclipse.microprofile.openapi.annotations.security.SecurityScheme;
import org.eclipse.microprofile.openapi.annotations.servers.Server;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.eclipse.microprofile.openapi.annotations.tags.Tags;
import org.eclipse.microprofile.openapi.apps.airlines.model.Booking;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;

@Path("/bookings")
@Tag(ref = "Bookings")
@Tags(value = @Tag(name = "Reservations", description = "All the reservation methods"))
@SecurityScheme(securitySchemeName = "bookingSecurityScheme", type = SecuritySchemeType.OPENIDCONNECT,
                description = "Security Scheme for booking resource",
                openIdConnectUrl = "http://openidconnect.com/testurl")
@Server(description = "Secure server", url = "https://gigantic-server.com:443")
@Server(description = "Unsecure server", url = "http://gigantic-server.com:80")
public class BookingResource {
    private Map<Integer, Booking> bookings = new ConcurrentHashMap<Integer, Booking>();
    private volatile int currentId = 0;

    @GET
    @Tag(ref = "bookings")
    @APIResponses(value = {
            @APIResponse(responseCode = "200", description = "Bookings retrieved",
                         content = @Content(schema = @Schema(type = SchemaType.ARRAY, implementation = Booking.class))),
            @APIResponse(responseCode = "404", description = "No bookings found for the user.")
    })
    @Operation(summary = "Retrieve all bookings for current user", operationId = "getAllBookings")
    @Produces("application/json")
    public Response getBookings() {
        return Response.ok().entity(bookings.values()).build();
    }

    @POST
    @SecurityRequirement(name = "bookingSecurityScheme", scopes = {"write:bookings", "read:bookings"})
    @Callback(name = "bookingCallback", callbackUrlExpression = "http://localhost:9080/airlines/bookings",
              operations = @CallbackOperation(method = "get", summary = "Retrieve all bookings for current user",
                                              responses = {
                                                      @APIResponse(responseCode = "200",
                                                                   description = "Bookings retrieved",
                                                                   content = @Content(mediaType = "application/json",
                                                                                      schema = @Schema(type = SchemaType.ARRAY,
                                                                                                       implementation = Booking.class))),
                                                      @APIResponse(responseCode = "404",
                                                                   description = "No bookings found for the user.")
                                              }))
    @APIResponse(responseCode = "201", description = "Booking created",
                 content = @Content(schema = @Schema(name = "id", description = "id of the new booking",
                                                     type = SchemaType.STRING)))
    @Operation(summary = "Create a booking",
               description = "Create a new booking record with the booking information provided.",
               operationId = "createBooking")
    @Consumes("application/json")
    @Produces("application/json")
    public Response createBooking(
            @RequestBody(description = "Create a new booking with the provided information.",
                         content = @Content(mediaType = "application/json", schema = @Schema(ref = "Booking"),
                                            examples = @ExampleObject(name = "booking",
                                                                      summary = "External booking example",
                                                                      externalValue = "http://foo.bar/examples/booking-example.json")),
                         extensions = @Extension(name = "x-request-body", value = "test-request-body")) Booking task) {
        bookings.put(currentId, task);
        return Response.status(Status.CREATED).entity("{\"id\":" + currentId++ + "}").build();
    }

    @GET
    @Path("{id}")
    @Parameters({
            @Parameter(name = "id", description = "ID of the booking", required = true, in = ParameterIn.PATH,
                       style = ParameterStyle.SIMPLE)
    })

    @Produces("application/json")
    @Operation(summary = "Get a booking with ID", operationId = "getBookingById")
    @APIResponses(value = {
            @APIResponse(responseCode = "200", description = "Booking retrieved",
                         content = @Content(schema = @Schema(implementation = Booking.class))),
            @APIResponse(responseCode = "404", description = "Booking not found")
    })
    public Response getBooking(
            @PathParam("id") int id) {
        Booking booking = bookings.get(id);
        if (booking != null) {
            return Response.ok().entity(booking).build();
        } else {
            return Response.status(Status.NOT_FOUND).build();
        }
    }

    @PUT
    @Path("{id}")
    @Consumes("application/json")
    @Produces("text/plain")
    @APIResponse(responseCode = "200", description = "Booking updated")
    @APIResponse(responseCode = "404", description = "Booking not found")
    @Operation(summary = "Update a booking with ID", operationId = "updateBookingId")
    public Response updateBooking(
            @PathParam("id") int id, Booking booking) {
        if (bookings.get(id) != null) {
            bookings.put(id, booking);
            return Response.ok().build();
        } else {
            return Response.status(Status.NOT_FOUND).build();
        }
    }

    @DELETE
    @Path("{id}")
    @Tag()
    @APIResponse(responseCode = "200", description = "Booking deleted successfully.")
    @APIResponse(responseCode = "404", description = "Booking not found.")
    @Operation(summary = "Delete a booking with ID", operationId = "deleteBookingById")
    @Produces("text/plain")
    public Response deleteBooking(
            @PathParam("id") int id) {
        if (bookings.get(id) != null) {
            bookings.remove(id);
            return Response.ok().build();
        } else {
            return Response.status(Status.NOT_FOUND).build();
        }
    }
}
